package cn.hg.jbsa.admin.controller;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresGuest;
import org.apache.shiro.authz.annotation.RequiresUser;
import org.apache.shiro.subject.Subject;

import com.jfinal.aop.Before;
import com.jfinal.aop.Inject;
import com.jfinal.log.Log;

import cn.hg.jboot.core.BeanStatus;
import cn.hg.jboot.core.exception.BaseServiceException;
import cn.hg.jboot.core.kit.PasswordKit;
import cn.hg.jbsa.admin.controller.system.validator.JbsaChangePasswordValidator;
import cn.hg.jbsa.admin.controller.system.validator.JbsaLoginValidator;
import cn.hg.jbsa.admin.model.JbsaAdmin;
import cn.hg.jbsa.admin.model.JbsaResource;
import cn.hg.jbsa.admin.service.JbsaAdminService;
import cn.hg.jbsa.admin.service.JbsaResourceService;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import io.jboot.utils.RequestUtil;
import io.jboot.web.controller.annotation.GetRequest;
import io.jboot.web.controller.annotation.PostRequest;
import io.jboot.web.controller.annotation.RequestMapping;

/**
 * @author golly
 * @date 2021/10/18
 */
@RequestMapping(value = "/admin")
public class JbsaIndexController extends BaseJbsaAdminController {
    protected static String SERVICE_NAME = "JBootSimpleAdmin：";

    private Log LOG = Log.getLog(JbsaIndexController.class);

    @Inject
    private JbsaAdminService adminService;

    @Inject
    private JbsaResourceService resourceService;

    private List<JSONObject> buildChildrenMenuJsonObject(List<JbsaResource> menuList) {
        List<JSONObject> jsonList = new ArrayList<>();
        for (JbsaResource jbsaResource : menuList) {
            if (!CharSequenceUtil.equals("menu", jbsaResource.getType())) {
                continue;
            }
            if (CharSequenceUtil.isNotBlank(jbsaResource.getValue())) {
                JSONObject object = JSONUtil.parseObj(jbsaResource.getValue());
                jsonList.add(object);

                List<JbsaResource> children = jbsaResource.get("children");
                if (CollUtil.isNotEmpty(children)) {
                    object.set("children", this.buildChildrenMenuJsonObject(children));
                }
            }
        }

        return jsonList;
    }

    private List<JSONObject> buildMenuJsonObject(Integer... parentIds){
        List<JbsaResource> menuList = this.resourceService.findMenuByAdmin(this.getCurrentAdmin(), BeanStatus.on.name(), parentIds);

        List<JSONObject> jsonList = this.buildChildrenMenuJsonObject(menuList);

        return jsonList;
    }

    /**
     * 获取图形验证码功能
     */
    public void captcha() {
        this.LOG.debug("{}获取图形验证码功能：", SERVICE_NAME);
        this.renderCaptcha();
    }

    /**
     * 打开修改密码页面
     */
    @RequiresUser
    public void changePassword() {
        this.LOG.info("{}打开修改密码页面：", SERVICE_NAME);
    }

    /**
     * 执行修改密码操作
     */
    @PostRequest
    @RequiresUser
    @Before(JbsaChangePasswordValidator.class)
    public void doChangePassword() {
        this.LOG.info("{}执行修改密码操作：", SERVICE_NAME);
        JbsaAdmin admin = this.getCurrentAdmin();

        if (!PasswordKit.validatePassword(admin.getSalt(), this.getPara("oldPassword"), admin.getPassword())) {
            this.renderAjaxFailed("旧密码不正确！");
            return;
        }

        admin.setPassword(PasswordKit.genSecretPassword(admin.getSalt(), this.getPara("newPassword")));

        this.adminService.update(admin);

        this.renderAjaxSuccessed();
    }

    /**
     * 登录操作
     */
    @Before(JbsaLoginValidator.class)
    public void doLogin(String account, String password) {
        UsernamePasswordToken token = new UsernamePasswordToken(account, password, false, this.getIPAddress());

        try {
            Subject subject = SecurityUtils.getSubject();

            //是否需要进行验证
            if (!subject.isAuthenticated()) {
                subject.login(token);
            }

            // 更新用户登录信息
            this.updateAdmin(this.getCurrentAdmin());

            this.renderAjaxSuccessed();
        } catch (AuthenticationException e) {
            this.LOG.info("登录时发生异常：message={}", e.getMessage(), e.getCause());
            this.renderAjaxFailed(e.getMessage());
        } catch (BaseServiceException e) {
            this.LOG.warn("登录时发生异常：code={}，message={}", e.getCode(), e.getMessage(), e.getCause());
            this.renderAjaxFailed(e.getMessage());
        } catch (Throwable e) {
            this.LOG.error("登录时发生异常：message={}", e.getMessage(), e.getCause());
            this.renderAjaxFailed("登录认证异常!");
        }

    }

    /**
     * 登出操作
     */
    public void doLogout() {
        this.LOG.info("{}执行登出操作：", SERVICE_NAME);

        if (SecurityUtils.getSubject().isAuthenticated()) {
            SecurityUtils.getSubject().logout();
        }

        if (RequestUtil.isAjaxRequest(this.getRequest())) {
            this.renderAjaxSuccessed();
        } else {
            this.redirect("login");
        }
    }

    /**
     * 首页
     */
    @RequiresAuthentication
    public void home() {

    }

    /*
     * （非 Javadoc）
     * @see cn.hg.base.controller.BaseController#index()
     */
    @RequiresAuthentication
    @Override
    public void index() {
        this.LOG.info("{}打开管理端页面：", SERVICE_NAME);
        this.setAttr("sysProp", System.getProperties());
        this.setAttr("admin", this.getCurrentAdmin());
    }

    /**
     * 跳转入登录页面
     */
    @RequiresGuest
    public void login() {
        this.LOG.info("{}跳转入登录页面：", SERVICE_NAME);
    }

    @RequiresAuthentication
    @GetRequest
    public void menu() {
        this.LOG.debug("{}打开管理端菜单：", SERVICE_NAME);

        String[] ids = CharSequenceUtil.splitToArray(this.get(0), ',');
        Integer[] parentIds = new Integer[CollUtil.size(ids)];
        for (int i = 0; i < ids.length; i++) {
            parentIds[i] = NumberUtil.parseInt(ids[i]);
        }

        this.renderJson(this.buildMenuJsonObject(parentIds));
    }

    private void updateAdmin(JbsaAdmin admin) {
        // 登录成功则更新用户登录信息
        admin.setLoginIp(this.getIPAddress());
        admin.setLoginTime(new Date());
        admin.setTotalLogins(admin.getTotalLogins() + 1);

        this.adminService.update(admin);
    }
}
